package com.douqu.game.battle.controller;

import com.douqu.game.battle.entity.BattleMasterAObject;
import com.douqu.game.battle.entity.BattleSoldierAObject;
import com.douqu.game.battle.entity.PlayerBattleTmp;
import com.douqu.game.battle.server.GameServer;
import com.douqu.game.core.e.E_BattleAObjectStatus;
import com.douqu.game.core.e.E_PlayerStatus;
import com.douqu.game.core.entity.Skill;
import com.douqu.game.core.entity.battle.*;
import com.douqu.game.core.entity.InitData;
import com.douqu.game.core.controller.BattleParentController;
import com.douqu.game.core.controller.PlayerController;
import com.douqu.game.core.controller.SpriteController;
import com.douqu.game.core.entity.sprite.Card;
import com.douqu.game.core.factory.ConstantFactory;
import com.douqu.game.core.factory.DataFactory;
import com.douqu.game.core.factory.WordFactory;
import com.douqu.game.core.i.IMainServer;
import com.douqu.game.core.protobuf.SGCommonProto;
import com.douqu.game.core.protobuf.SGCommonProto.*;
import com.douqu.game.core.protobuf.SGMainProto.*;
import com.douqu.game.core.protobuf.SGWarProto;
import com.douqu.game.core.util.SendUtils;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.log4j.Logger;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;


/**
* Created by bean on 2017/7/18.
*/
public class BattleController extends BattleParentController {

    private Logger logger = Logger.getLogger(BattleController.class);

    /** 公共CD时间 */
    public final static int PUBLIC_CD_TIME = 4 * GameServer.UPDATE_BATTLE_TIME;

    /**
     * 场上的所有兵
     */
    protected List<BattleAObject> soldiers;

    protected Map<Integer, BattleAObject> soldierMap;


    public BattleController(String battleId, PlayerController player, SpriteController target, E_BATTLE_TYPE battleType,
                            SGCommonProto.Pos leftBottom, SGCommonProto.Pos rightTop)
    {
        super(battleId, player, target, battleType, leftBottom, rightTop);
    }

    @Override
    public void init()
    {
        super.init();

        InitData initData = DataFactory.getInstance().getInitData();
        this.maxBattleTime = initData.crystalGrow[initData.crystalGrow.length-1].id;
//        this.maxBattleTime = 5000;

        this.createTime = (int) (GameServer.currentTime/1000);

        //队伍1的主将
        BattleMasterAObject masterAObject1 = new BattleMasterAObject(player, createMasterUniqueId + BattleController.TEAM_1);
        masterAObject1.setTeamNo(BattleController.TEAM_1);
        masterAObject1.setPosition(getMiddlePos(BattleController.TEAM_1));

        SpriteBattleTmp pbt1 = new PlayerBattleTmp();
        pbt1.setBattleParent(this);
        pbt1.init(player, masterAObject1, BattleController.TEAM_1);
        player.setAttachment(pbt1);

        playerList.add(player);

        soldiers = new CopyOnWriteArrayList<>();
        soldierMap = new ConcurrentHashMap<>();

        battleInfo = new BattleControllerInfo();
        battleInfo.setId(id);
        battleInfo.setCreateTime(createTime);
        battleInfo.setBattleType(battleType.getNumber());
        battleInfo.setTeam1PlayerIndex(player.getObjectIndex());
        battleInfo.setTeam2PlayerIndex(target.getObjectIndex());
    }


    /**
     * 是否过期
     * @return
     */
    public boolean isOverdue()
    {
        return startTime == 0 && GameServer.currentTime/1000 - createTime >= 60;
    }


    @Override
    public void addSoldier(BattleAObject battleAObject)
    {
        soldiers.add(battleAObject);
        soldierMap.put(battleAObject.getId(), battleAObject);
    }

    public List<BattleAObject> getSoldiers()
    {
        return soldiers;
    }


    /**
     *
     * @param currentTime 当前时间
     */
    public void update(long currentTime)
    {
        if(startTime <= 0)
        {
//            logger.info("还没开始战斗呢!" + id);
            return;
        }

        battleTime += GameServer.UPDATE_BATTLE_TIME;

        if(battleTime >= maxBattleTime)//战斗时间到了
        {
            //战斗时间到了,检测胜负
            int winTeam = getWinTeam();
            logger.info("战斗时间到了，胜利方:" + winTeam);
            end(winTeam);
        }
        else//时间没到
        {
            player.getAttachment().update(currentTime, battleTime, GameServer.UPDATE_BATTLE_TIME);

            if(checkEnd())
            {
                return;
            }
            target.getAttachment().update(currentTime, battleTime, GameServer.UPDATE_BATTLE_TIME);

            if(checkEnd())
            {
                return;
            }
        }
    }



    private boolean checkEnd()
    {
        if(isEnd())
        {
            end(getWinTeam());
            return true;
        }

        return false;
    }




    private boolean checkGrid(int x, int y)
    {
        if(x < 0 || y < 0 || x >= BattleController.MAX_ROW || y >= BattleController.MAX_COL)
            return false;

        return true;
    }


    public void ready(PlayerController playerController)
    {
        if(playerController.getStatus() != E_PlayerStatus.BATTLE_WAIT_START)
            return;

        SpriteBattleTmp spriteBattleTmp = playerController.getAttachment();
        if(spriteBattleTmp == null)
            return;

        spriteBattleTmp.setReady(true);

        if(isReady())
        {
            player.setStatus(E_PlayerStatus.BATTLING);
            target.setStatus(E_PlayerStatus.BATTLING);

            startTime = (int) (GameServer.currentTime/1000);
            battleInfo.setStartTime(startTime);
            battleTime = 0;

            IMainServer server = GameServer.getInstance().getiMainServer();
            if(server != null)
            {
                try {
                    server.battleStart(GameServer.getInstance().getId(), battleInfo);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            dispatchMsg(E_MSG_ID.MsgID_War_ReadyStart_VALUE, null);
        }
    }

    @Override
    public void end(int winTeam)
    {
        logger.info("战斗结束，胜利方:" + winTeam + " id:" + id);
        if(winTeam < 0)
            return;

        endTime = (int) (GameServer.currentTime/1000);

        battleInfo.setEndTime(endTime);
        battleInfo.setWinTeam(winTeam);

        int star1 = getStar(player.getAttachment());
        battleInfo.setTeam1Star(star1);

        int star2 = getStar(target.getAttachment());
        battleInfo.setTeam2Star(star2);

        SGWarProto.S2C_SynResult.Builder data = SGWarProto.S2C_SynResult.newBuilder();
        data.setWinTeam(winTeam);
        data.setStar(star1);
        player.sendMsg(E_MSG_ID.MsgID_War_SynResult_VALUE, data.build().toByteArray());

        data = SGWarProto.S2C_SynResult.newBuilder();
        data.setWinTeam(winTeam);
        data.setStar(star2);
        target.sendMsg(E_MSG_ID.MsgID_War_SynResult_VALUE, data.build().toByteArray());

        //通知双方战斗结束，计算战斗奖励
        player.clearBattle();
        target.clearBattle();

        GameServer.getInstance().getWorldManager().removeBattleController(this);

        try{
            GameServer.getInstance().getiMainServer().battleEnd(GameServer.getInstance().getId(), battleInfo);
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    public int getStar(SpriteBattleTmp spriteBattleTmp)
    {
        return 0;
    }


    public int getWinTeam()
    {
        if(player == null || player.getAttachment() == null || target == null || target.getAttachment() == null)
            return -1;

        if(player.getAttachment().isDie())
            return player.getAttachment().getTeamNo();
        else if(target.getAttachment().isDie())
            return target.getAttachment().getTeamNo();
        else
            return 0;
    }


    @Override
    public void removeBattleSoldier(BattleAObject soldier)
    {
        soldiers.remove(soldier);
        soldierMap.remove(soldier.getId());

        if(soldier.getTeamNo() == TEAM_1)
        {
            player.getAttachment().removeSoldier(soldier);
        }
        else
        {
            target.getAttachment().removeSoldier(soldier);
        }
    }



    @Override
    public List<BattleAObject> getSkillTargets(BattleSkill battleSkill)
    {
        List<BattleAObject> temp = new CopyOnWriteArrayList<>();
        Skill skill = battleSkill.getSkill();
        if(skill.effectTargetType == ConstantFactory.TARGET_TYPE_ALL)
        {
            temp.addAll(soldiers);
            temp.add(player.getAttachment().getMasterSoldier());
            temp.add(target.getAttachment().getMasterSoldier());
        }
        else if(skill.effectTargetType == ConstantFactory.TARGET_TYPE_PARTNER)
        {
            temp.addAll(battleSkill.getMasterController().getAttachment().getSoldierList());
            temp.add(battleSkill.getMasterController().getAttachment().getMasterSoldier());
        }
        else if(skill.effectTargetType == ConstantFactory.TARGET_TYPE_ENEMY)
        {
            temp.addAll(battleSkill.getMasterController().getAttachment().getTargetBattleTmp().getSoldierList());
            temp.add(battleSkill.getMasterController().getAttachment().getTargetBattleTmp().getMasterSoldier());
        }
        else if(skill.effectTargetType == ConstantFactory.TARGET_TYPE_SELF)
        {
            temp.add(battleSkill.getReleaser());
            return temp;
        }


        if(temp != null)
        {
            List<BattleAObject> result = new CopyOnWriteArrayList<>();
            for(BattleAObject battleAObject : temp)
            {
                if(skill.checkUnitType(battleAObject) && skill.isInAtkRange(battleSkill.getReleaser(), battleAObject))
                    result.add(battleAObject);

                if(skill.isSingleTarget() && result.size() > 0)
                    break;

            }
            return result;
        }

        return null;
    }

    @Override
    public List<BattleAObject> getSkillTargets(BattleSkill battleSkill, Position position)
    {
        List<BattleAObject> temp = new CopyOnWriteArrayList<>();
        Skill skill = battleSkill.getSkill();
        if(skill.effectTargetType == ConstantFactory.TARGET_TYPE_ALL)
        {
            temp.addAll(soldiers);
            temp.add(player.getAttachment().getMasterSoldier());
            temp.add(target.getAttachment().getMasterSoldier());
        }
        else if(skill.effectTargetType == ConstantFactory.TARGET_TYPE_PARTNER)
        {
            temp.addAll(battleSkill.getMasterController().getAttachment().getSoldierList());
            temp.add(battleSkill.getMasterController().getAttachment().getMasterSoldier());
        }
        else if(skill.effectTargetType == ConstantFactory.TARGET_TYPE_ENEMY)
        {
            temp.addAll(battleSkill.getMasterController().getAttachment().getTargetBattleTmp().getSoldierList());
            temp.add(battleSkill.getMasterController().getAttachment().getTargetBattleTmp().getMasterSoldier());
        }
        else if(skill.effectTargetType == ConstantFactory.TARGET_TYPE_SELF)
        {
            temp.add(battleSkill.getReleaser());
            return temp;
        }


        if(temp != null)
        {
            List<BattleAObject> result = new CopyOnWriteArrayList<>();
            for(BattleAObject battleAObject : temp)
            {
                if(skill.checkUnitType(battleAObject) && skill.isInAtkRange(position, battleAObject.getTeamNo(), battleAObject))
                    result.add(battleAObject);

                if(skill.isSingleTarget() && result.size() > 0)
                    break;
            }

            return result;
        }

        return null;
    }

    @Override
    public List<BattleAObject> getSkillTargets(BattleSkill battleSkill, List<BattleAObject> targets, boolean checkRange)
    {
        if(battleSkill.getSkill().effectTargetType == ConstantFactory.TARGET_TYPE_ALL)
            return targets;

        List<BattleAObject> result = new CopyOnWriteArrayList<>();
        for(BattleAObject battleAObject : targets)
        {
            if(battleSkill.skill.effectTargetType == ConstantFactory.TARGET_TYPE_SELF)
            {
                if(battleAObject.getId() == battleSkill.getReleaser().getId())
                {
                    result.add(battleAObject);
                    return result;
                }
            }
            else if(battleSkill.skill.effectTargetType == ConstantFactory.TARGET_TYPE_PARTNER)
            {
                if(battleAObject.getTeamNo() == battleSkill.getReleaser().getTeamNo() && battleSkill.checkUnitType(battleAObject))
                    result.add(battleAObject);
            }
            else if(battleSkill.skill.effectTargetType == ConstantFactory.TARGET_TYPE_ENEMY)
            {
                if(battleAObject.getTeamNo() != battleSkill.getReleaser().getTeamNo() && battleSkill.checkUnitType(battleAObject))
                    result.add(battleAObject);
            }
        }
        if(checkRange)
        {
            for(BattleAObject battleAObject : result)
            {
                if(!battleSkill.isInAtkRange(battleSkill.getReleaser(), battleAObject))
                    result.remove(battleAObject);
            }
        }

        if(battleSkill.skill.isSingleTarget())
        {
            if(result.size() > 0)
            {
                List<BattleAObject> tempResult = new CopyOnWriteArrayList<>();
                tempResult.add(result.get(0));
                return tempResult;
            }
        }

        return result;
    }


    //---------------------------------------------消息通道-----------------------------------------------------------------------
    /**
     * 上战场
     * @param playerController
     * @param data
     */
    public void toBattlefield(PlayerController playerController, byte[] data) throws InvalidProtocolBufferException
    {

//        List<Grid> targetList = request.getSoldierGridList();
//        for(Grid grid : targetList)
//        {
//            if(grid.getX() < 0 || grid.getY() >= MAX_ROW || grid.getX() < 0 || grid.getY() >= MAX_COL)
//            {
//                //参数错误
//                return;
//            }
//        }

        if(isEnd())
        {
            logger.info("战斗已经结束");
            playerController.sendAlert(E_NOTIFY_TYPE.NOTIFY_TYPE_WARN, WordFactory.getWord(WordFactory.ILLEGAL_ERROR));
            return;
        }

        PlayerBattleTmp playerBattleTmp = (PlayerBattleTmp) playerController.getAttachment();
        if(playerBattleTmp == null)
        {
            //错误
            logger.info("非法数据");
            playerController.sendAlert(E_NOTIFY_TYPE.NOTIFY_TYPE_WARN, WordFactory.getWord(WordFactory.ILLEGAL_ERROR));
            return;
        }
        SGWarProto.C2S_ToBattle request = SGWarProto.C2S_ToBattle.parseFrom(data);
        Card card = playerBattleTmp.getCardById(request.getCardId());
        if(card == null)
        {
            //没有此卡牌
            logger.info("卡牌不存在");
            playerController.sendAlert(E_NOTIFY_TYPE.NOTIFY_TYPE_WARN, WordFactory.getWord(WordFactory.OBJECT_NOT_EXIST));
            return;
        }
        List<Pos> posList = request.getPosList();
        if(posList.size() != card.soldierCount)
        {
            //参数错误
            logger.info("位置数量不对");
            playerController.sendAlert(E_NOTIFY_TYPE.NOTIFY_TYPE_WARN, WordFactory.getWord(WordFactory.ILLEGAL_ERROR));
            return;
        }
        if(playerBattleTmp.getCurCrystal() < card.crystal)
        {
            //水晶不够
            logger.info("水晶不够");
            playerController.sendAlert(E_NOTIFY_TYPE.NOTIFY_TYPE_WARN, WordFactory.getWord(WordFactory.ENERGY_NOT_ENOUGH));
            return;
        }

        //TODO 位置范围检测

        playerBattleTmp.soldierBorn(card,posList);


        if(!isRealPlayerBattle())
        {
            //检测AI
            logger.info("对手开始检测AI出牌：" + target);
            target.getAttachment().checkAI();
        }
    }




//    /**
//     * 场上的兵开始战斗
//     * @param source
//     * @param target
//     */
//    private void soldierBattle(BattleGrid source, BattleGrid target)
//    {
//        if(source.battleAObject == null || target.battleAObject == null)
//            return;
//
//        if(source.battleAObject.isDie() || target.battleAObject.isDie())
//            return;
//
//        source.battleAObject.addAtkTarget(target.battleAObject);
//        //TODO 告诉对方士兵进入战斗
//
//        SGWarProto.S2C_ToBattle.Builder response = SGWarProto.S2C_ToBattle.newBuilder();
//
//        dispatchMsg(SGMainProto.E_MSG_ID.MsgID_War_ToBattle_VALUE, response.build().toByteArray());
//    }
//
//
//    /**
//     * 场上的兵被kill
//     */
//    public void killSoldier(PlayerController playerController, byte[] data)
//    {
//        int soldierId = 0;//获取参数,(兵种ID)
//        BattleAObject battleAObject = soldierMap.get(soldierId);
//        if(battleAObject == null)
//        {
//            //没有这个兵
//            return;
//        }
//
//        PlayerBattleTmp playerBattleTmp = (PlayerBattleTmp) playerController.getAttachment();
//        if(playerBattleTmp.getTeamNo() == battleAObject.getTeamNo())
//        {
//            //目前设定，不能自己杀死自己的兵
//            return;
//        }
//
//        battleAObject.setHp(0);
//        soldiers.remove(battleAObject);
//        soldierMap.remove(soldierId);
//
//        if(battleAObject instanceof BattleMasterAObject)
//        {
//            //主将死了，战斗结束
//            //胜利队伍
//            end(playerBattleTmp.getTeamNo());
//        }
//        else
//        {
//            //普通的兵死亡
//        }
//    }


    /**
     * 同步位置
     * @param playerController
     * @param data
     */
    private void synPos(PlayerController playerController, byte[] data) throws InvalidProtocolBufferException
    {
        SGWarProto.C2S_SynPos request = SGWarProto.C2S_SynPos.parseFrom(data);

//        request.get
    }

    /**
     * 获取到兵的路线
     * @param playerController
     * @param data
     */
    private void soldierRoute(PlayerController playerController, byte[] data) throws InvalidProtocolBufferException
    {
        SGWarProto.C2S_SoldierRoute routeData = SGWarProto.C2S_SoldierRoute.parseFrom(data);

        BattleAObject battleAObject = soldierMap.get(routeData.getUniqueId());
        if(battleAObject == null)
        {
            //错误
            return;
        }

        if(!(battleAObject instanceof BattleSoldierAObject))
        {
            return;
        }

        BattleSoldierAObject soldier = (BattleSoldierAObject) battleAObject;

        synchronized (soldier)
        {
            List<Position> list = new CopyOnWriteArrayList<>();
            for(Pos pos : routeData.getRouteList())
            {
                list.add(new Position(pos));
            }
            soldier.initRoute(list);

            soldier.setStatus(E_BattleAObjectStatus.GOING);


            logger.info("获取路线:" + soldier + " 路线:" + list);
        }


    }


    /**
     * 主将使用技能
     * @param playerController
     * @param data
     */
    public void useSkill(PlayerController playerController, byte[] data) throws InvalidProtocolBufferException
    {
        SGWarProto.C2S_UseSkill request = SGWarProto.C2S_UseSkill.parseFrom(data);

        PlayerBattleTmp playerBattleTmp = (PlayerBattleTmp) playerController.getAttachment();
        if(playerBattleTmp.getPublicCDTime() > 0)
        {
            //公共CD时间没到
            playerController.sendAlert(E_NOTIFY_TYPE.NOTIFY_TYPE_WARN, WordFactory.getWord(WordFactory.CD_TIME_ERROR));
            return;
        }

        BattleSkill skill = playerBattleTmp.getMasterSoldier().getCDSkill(request.getSkillId());
        if(skill == null)
        {
            //没有这个技能
            playerController.sendAlert(E_NOTIFY_TYPE.NOTIFY_TYPE_WARN, WordFactory.getWord(WordFactory.SKILL_NOT_EXIST));
            return;
        }

        if(playerBattleTmp.getCurCrystal() < skill.getSkill().crystal)
        {
            //水晶不够
            playerController.sendAlert(E_NOTIFY_TYPE.NOTIFY_TYPE_WARN, WordFactory.getWord(WordFactory.ENERGY_NOT_ENOUGH));
            return;
        }

        if(skill.getSkill().type != ConstantFactory.SKILL_TYPE_MASTER)
        {
            //非主将技能不能手动释放
            playerController.sendAlert(E_NOTIFY_TYPE.NOTIFY_TYPE_WARN, WordFactory.getWord(WordFactory.ILLEGAL_ERROR));
            return;
        }

        if(!skill.isCanReleaseMasterSkill())
        {
            //cd时间没到
            playerController.sendAlert(E_NOTIFY_TYPE.NOTIFY_TYPE_WARN, WordFactory.getWord(WordFactory.CD_TIME_ERROR));
            return;
        }

//        List<Integer> targetIds = request.getTargetIdList();
        Pos pos = request.getPos();
        if(pos == null)
//        if((targetIds == null || targetIds.size() == 0) && pos == null)
        {
            //参数错误
            return;
        }
        //技能目标列表
//        List<BattleAObject> targets = new CopyOnWriteArrayList<>();
//        if(targetIds != null && targetIds.size() > 0)
//        {
//            BattleAObject battleAObject = null;
//            for(Integer id : targetIds)
//            {
//                battleAObject = soldierMap.get(id);
//                if(battleAObject != null)
//                {
//                    targets.add(battleAObject);
//                }
//            }
//
//            targets = getSkillTargets(skill, targets, false);
//        }

        skill.release(null, new Position(request.getPos()), GameServer.currentTime);

        //释放成功
        skill.initCDTime();
        playerBattleTmp.setPublicCDTime(PUBLIC_CD_TIME);
        playerBattleTmp.addCrystal(-skill.getSkill().crystal);

    }




    /** 消息通道
     */
    public void messageChannel(int code, PlayerController playerController, byte[] data)
    {
        SpriteBattleTmp battleTmp = playerController.getAttachment();
        if(battleTmp == null)
            return;

        if(battleTmp.getTeamNo() == BattleController.TEAM_SPECTATOR)
        {
            //观战者
            return;
        }

        logger.info(playerController.getName() + "  当前玩家状态："+playerController.getStatus() + "  " + playerController.getParent());
        if(!playerController.isBattling())
            return;

        try{
            switch (code)
            {
                case E_MSG_ID.MsgID_War_ReadyStart_VALUE:
                    if(playerController.getStatus() != E_PlayerStatus.BATTLE_WAIT_START)
                        return;
                    ready(playerController);
                    break;
                case E_MSG_ID.MsgID_War_ToBattle_VALUE://出牌
                    if(playerController.getStatus() != E_PlayerStatus.BATTLING)
                        return;
                    toBattlefield(playerController, data);
                    break;
                case E_MSG_ID.MsgID_War_SoldierRoute_VALUE://获取路线
                    if(playerController.getStatus() != E_PlayerStatus.BATTLING)
                        return;
                    soldierRoute(playerController, data);
                    break;
                case E_MSG_ID.MsgID_War_SynPos_VALUE://同步位置
                    if(playerController.getStatus() != E_PlayerStatus.BATTLING)
                        return;
                    synPos(playerController, data);
                    break;
                case E_MSG_ID.MsgID_War_UseSkill_VALUE://使用技能
                    if(playerController.getStatus() != E_PlayerStatus.BATTLING)
                        return;
                    useSkill(playerController, data);
                    break;
            }
        }catch (Exception e){
            logger.info("Error: code:" + code +" " + e.getLocalizedMessage());
            e.printStackTrace();
        }

    }


}
